
#include "acl_cpp/lib_acl.hpp"
#include "lib_acl.h"

/**
 * author: niukey@qq.com
 * date: 2015.1.27
 * modify by zsx
 */

static const char *__key = "list";
static const int max_print_line = 50;
static const int __value_len = 32;

#define DELETE_VALUES(values, n)\
do\
{  for(int i = 0; i < (n) ; ++i)\
    delete [](values)[i];\
   delete [](values);\
} while (0);


static bool test_lpush(acl::redis_list &redis, int n)
{
    acl::string value;

    for (int i = 0; i < n; i++) {
        value.format("%s_%d", __key, i);
        redis.clear();
        int ret = redis.lpush(__key, value.c_str(), NULL);
        if (ret <= 0) {
            printf("lpush key: %s error: %s, ret: %d\r\n",
                   __key, redis.result_error(), ret);
            return false;
        } else if (i < max_print_line)
            printf("lpush ok, key:%s, value:%s, ret: %d\r\n",
                   __key, value.c_str(), ret);
    }

    return true;
}

static bool test_lpush2(acl::redis_list &redis, int n)
{
    char **value = new char *[n + 1];
    for (int i = 0; i < n; i++) {
        value[i] = new char[__value_len];
        acl::safe_snprintf(value[i], __value_len, "%s_%d", __key, i);
    }
    value[n] = NULL;

    redis.clear();
    int ret = redis.lpush(__key, (const char **) value, n);
    if (ret <= 0) {
        printf("lpush key: %s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("lpush key:%s ret : %d ok\r\n", __key, ret);

    for (int i = 0; i < n; ++i)
        delete value[i];

    delete[] value;

    return true;
}

static bool test_lpush3(acl::redis_list &redis, int n)
{
    std::vector<acl::string> values;
    acl::string buf;

    for (int i = 0; i < n; ++i) {
        buf.format("%s_%d", __key, i);
        values.push_back(buf);
    }

    redis.clear();
    int ret = redis.lpush(__key, values);
    if (ret <= 0) {
        printf("lpush key:%s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("lpush key:%s ret : %d ok\r\n", __key, ret);

    return true;
}

static bool test_lpush4(acl::redis_list &redis, int n)
{
    std::vector<const char *> values;
    char **value = new char *[n];

    for (int i = 0; i < n; ++i) {
        value[i] = new char[__value_len];
        acl::safe_snprintf(value[i], __value_len, "%s_%d", __key, i);
        values.push_back(value[i]);
    }

    redis.clear();
    int ret = redis.lpush(__key, (std::vector<const char *> &) values);
    if (ret <= 0) {
        printf("lpush key:%s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("lpush key:%s ret : %d ok\r\n", __key, ret);

    for (int i = 0; i < n; ++i)
        delete value[i];

    delete[] value;

    return true;
}

static bool test_lpush5(acl::redis_list &redis, int n)
{
    std::vector<const char *> values;
    char **value = new char *[n];
    for (int i = 0; i < n; ++i) {
        value[i] = new char[__value_len];
        acl::safe_snprintf(value[i], __value_len, "%s_%d", __key, i);
        values.push_back(value[i]);
    }

    redis.clear();
    int ret = redis.lpush(__key, values);
    if (ret <= 0) {
        printf("lpush key:%s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("lpush key:%s ret : %d ok\r\n", __key, ret);

    for (int i = 0; i < n; ++i)
        delete value[i];

    delete[] value;

    return true;
}

static bool test_lpush6(acl::redis_list &redis, int n)
{
    char **values = new char *[n];
    size_t *lens = new size_t[n];
    for (int i = 0; i < n; ++i) {
        values[i] = new char[__value_len];
        acl::safe_snprintf(values[i], __value_len, "%s_%d", __key, i);
        lens[i] = strlen(values[i]);
    }

    redis.clear();
    int ret = redis.lpush(__key, (const char **) values, lens, n);
    if (ret <= 0) {
        printf("lpush key:%s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("lpush key:%s ret : %d ok\r\n", __key, ret);

    for (int i = 0; i < n; ++i)
        delete values[i];

    delete[] values;

    return true;
}

static bool test_rpush(acl::redis_list &redis, int n)
{
    acl::string value;
    int ret;
    for (int i = 0; i < n; ++i) {
        value.format("%s_%d", __key, i);
        redis.clear();
        ret = redis.rpush(__key, value.c_str(), NULL);
        if (ret <= 0) {
            printf("rpush key:%s error, %s\r\n",
                   __key, redis.result_error());
            return false;
        } else if (i < max_print_line) {
            printf("rpush key:%s, value:%s ok \r\n",
                   __key, value.c_str());
        }
    }

    return true;
}

static bool test_rpush2(acl::redis_list &redis, int n)
{
    char **values = new char *[n];
    for (int i = 0; i < n; ++i) {
        values[i] = new char[__value_len];
        acl::safe_snprintf(values[i], __value_len, "%s_%d", __key, i);
    }

    redis.clear();
    int ret = redis.rpush(__key, (const char **) values, n);
    if (ret < 0)
        printf("rpush key:%s error: %s\r\n", __key, redis.result_error());
    else
        printf("rpush key:%s ret : %d ok\r\n", __key, ret);

    DELETE_VALUES(values, n);

    return ret > 0 ? true : false;
}

static bool test_rpush3(acl::redis_list &redis, int n)
{
    std::vector<acl::string> values;
    acl::string value;
    for (int i = 0; i < n; ++i) {
        value.format("%s_%d", __key, i);
        values.push_back(value);
    }

    redis.clear();
    int ret = redis.rpush(__key, values);
    if (ret <= 0) {
        printf("rpush key:%s error: %s", __key, redis.result_error());
        return false;
    } else
        printf("rpush key:%s ret : %d ok\r\n", __key, ret);

    return true;
}

static bool test_rpush4(acl::redis_list &redis, int n)
{
    std::vector<const char *> values;
    for (int i = 0; i < n; ++i) {
        char *value = new char[__value_len];
        acl::safe_snprintf(value, __value_len, "%s_%d", __key, i);
        values.push_back(value);
    }

    redis.clear();
    int ret = redis.rpush(__key, (const std::vector<const char *> &) values);
    if (ret <= 0) {
        printf("rpush key:%s ok", __key);
        return false;
    } else
        printf("rpush key:%s ret : %d ok\r\n", __key, ret);

    while (values.empty() == false) {
        const char *value = values.back();
        values.pop_back();
        delete value;
    }

    return true;
}

static bool test_rpush5(acl::redis_list &redis, int n)
{
    std::vector<const char *> values;
    for (int i = 0; i < n; ++i) {
        char *value = new char[__value_len];
        acl::safe_snprintf(value, __value_len, "%s_%d", __key, i);
        values.push_back(value);
    }

    redis.clear();
    int ret = redis.rpush(__key, values);
    if (ret <= 0)
        printf("rpush key:%s ok", __key);
    else
        printf("rpush key:%s ret : %d ok\r\n", __key, ret);

    while (values.empty() == false) {
        const char *value = values.back();
        values.pop_back();
        delete value;
    }

    return ret > 0 ? true : false;
}

static bool test_rpush6(acl::redis_list &redis, int n)
{
    char **values = new char *[n];
    size_t *lens = new size_t[n];
    for (int i = 0; i < n; ++i) {
        values[i] = new char[__value_len];
        lens[i] = __value_len;
        acl::safe_snprintf(values[i], __value_len, "%s_%d", __key, i);
    }

    redis.clear();
    int ret = redis.rpush(__key, (const char **) values, lens, n);
    if (ret <= 0)
        printf("rpush key:%s error: %s", __key, redis.result_error());
    else
        printf("rpush key:%s ret : %d ok\r\n", __key, ret);

    DELETE_VALUES(values, n);
    delete[]lens;

    return ret > 0 ? true : false;
}

static bool test_lpushx(acl::redis_list &redis, int n)
{
    acl::string value;
    int ret;

    for (int i = 0; i < n; i++) {
        value.format("%s_%d", __key, i);
        redis.clear();
        ret = redis.lpushx(__key, value.c_str());
        if (ret <= 0) {
            printf("lpushx key: %s error: %s\r\n",
                   __key, redis.result_error());
            return false;
        } else if (i < max_print_line)
            printf("lpushx ok, key:%s ,value:%s \r\n",
                   __key, value.c_str());
    }

    return true;
}

static bool test_lpushx2(acl::redis_list &redis, int n)
{
    acl::string value;
    for (int i = 0; i < n; ++i) {
        value.format("%s_%d", __key, i);
        redis.clear();
        int ret = redis.lpushx(__key, value.c_str(), value.length());
        if (ret <= 0) {
            printf("lpushx key:%s error: %s\r\n",
                   __key, redis.result_error());
            return false;
        } else if (i < max_print_line)
            printf("lpushx key:%s ok", __key);
    }

    return true;

}

static bool test_rpushx(acl::redis_list &redis, int n)
{
    acl::string value;
    for (int i = 0; i < n; ++i) {
        value.format("%s_%d", __key, i);
        redis.clear();
        int ret = redis.rpushx(__key, value.c_str());
        if (ret <= 0) {
            printf("lpushx key:%s error: %s\r\n",
                   __key, redis.result_error());
            return false;
        } else if (i < max_print_line)
            printf("lpushx key:%s ok", __key);
    }

    return true;
}

static bool test_rpushx2(acl::redis_list &redis, int n)
{
    acl::string value;
    for (int i = 0; i < n; ++i) {
        value.format("%s_%d", __key, i);
        redis.clear();
        int ret = redis.rpushx(__key, value.c_str(), value.length());
        if (ret <= 0) {
            printf("lpushx key:%s error: %s\r\n",
                   __key, redis.result_error());
            return false;
        } else if (i < max_print_line)
            printf("lpushx key:%s ok\r\n", __key);
    }

    return true;
}

static bool test_lrange(acl::redis_list &redis)
{
    std::vector<acl::string> result;

    redis.clear();
    bool ret = redis.lrange(__key, 0, 1000, &result);
    if (ret == false) {
        printf("lrang key: %s error: %s\r\n",
               __key, redis.result_error());
        return false;
    }

    std::vector<acl::string>::const_iterator citr = result.begin();
    int i = 0;
    printf("lrang key: %s result:\r\n", __key);

    for (; citr != result.end(); ++citr) {
        printf("%s\r\n", (*citr).c_str());
        if (i >= max_print_line)
            break;
    }

    return true;
}

static bool test_rpop(acl::redis_list &redis, int n)
{
    int ret;
    acl::string buf;

    for (int i = 0; i < n; ++i) {
        redis.clear();
        buf.clear();
        ret = redis.rpop(__key, buf);
        if (ret <= 0) {
            printf("rpop key: %s error: %s\r\n",
                   __key, redis.result_error());
            return false;
        }
        printf("rpop key:%s ,buf:%s ok \r\n", __key, buf.c_str());
    }

    return true;
}

static bool test_lpop(acl::redis_list &redis, int n)
{
    int ret;
    acl::string buf;

    for (int i = 0; i < n; ++i) {
        redis.clear();
        buf.clear();
        ret = redis.lpop(__key, buf);
        if (ret <= 0) {
            printf("lpop key: %s error: %s\r\n",
                   __key, redis.result_error());
            return false;
        }
        printf("lpop key:%s ,buf:%s ok \r\n", __key, buf.c_str());
    }

    return true;
}

static bool test_blpop(acl::redis_list &redis, int n)
{
    std::vector<acl::string> keys;
    acl::string key;
    std::pair<acl::string, acl::string> result;

    for (int i = 0; i < n; ++i) {
        key.format("%s_%d", __key, i);
        keys.push_back(key);
    }
    bool ret = redis.blpop(keys, n, result);
    if (ret == false) {
        printf("blpop timeout: %s\r\n", redis.result_error());
        return false;
    } else
        printf("blpop key:%s,value:%s ok \r\n",
               result.first.c_str(), result.second.c_str());

    return true;
}

static bool test_brpop(acl::redis_list &redis, int n)
{
    std::vector<acl::string> keys;
    acl::string key;
    std::pair<acl::string, acl::string> result;

    for (int i = 0; i < n; ++i) {
        key.format("%s_%d", __key, i);
        keys.push_back(key);
    }

    redis.clear();
    bool ret = redis.brpop(keys, n, result);
    if (ret == false) {
        printf("brpop timeout: %s\r\n", redis.result_error());
        return false;
    } else
        printf("brpop key:%s,value:%s ok \r\n",
               result.first.c_str(), result.second.c_str());

    return true;
}

static bool test_rpoplpush(acl::redis_list &redis, int)
{
    acl::string src("list_src");
    acl::string dst("list_dst");
    acl::string buf;

    redis.clear();
    bool ret = redis.rpoplpush(src.c_str(), dst.c_str(), &buf);
    if (ret == false) {
        printf("rpoplpush src:%s, dst:%s error: %s\r\n",
               src.c_str(), dst.c_str(), redis.result_error());
        return false;
    } else
        printf("rpoplpush src:%s, dst:%s buf:%s ok \r\n",
               src.c_str(), dst.c_str(), buf.c_str());

    return true;

}

static bool test_brpoplpush(acl::redis_list &redis, int)
{
    acl::string src("list1");
    acl::string dst("list");
    acl::string buf;

    redis.clear();
    bool ret = redis.brpoplpush(src.c_str(), dst.c_str(), 1, &buf);
    if (ret == false) {
        printf("brpoplpush src:%s, dst:%s timeout: %s\r\n",
               src.c_str(), dst.c_str(), redis.result_error());
        return false;
    } else
        printf("rpoplpush src:%s, dst:%s buf:%s ok \r\n",
               src.c_str(), dst.c_str(), buf.c_str());

    return true;
}

static bool test_lrem(acl::redis_list &redis, int n)
{
    redis.clear();
    int ret = redis.lrem(__key, n, "list1_1");
    if (ret < 0) {
        printf("lrem key:%s,count:%d error: %s\r\n",
               __key, n, redis.result_error());
        return false;
    } else
        printf("lrem key:%s,count :%d ok\r\n", __key, n);

    return true;
}

static bool test_ltrim(acl::redis_list &redis, int n)
{
    redis.clear();
    bool ret = redis.ltrim(__key, 0, n);
    if (ret == false) {
        printf("ltrim key:%s,start:%d end:%d error: %s\r\n",
               __key, 0, n, redis.result_error());
        return false;
    } else
        printf("ltrim key:%s,start:%d end:%d ok\r\n", __key, 0, n);

    return true;
}

static bool test_llen(acl::redis_list &redis, int)
{
    redis.clear();
    int len = redis.llen(__key);
    if (len < 0) {
        printf("llen key:%s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("llen key:%s ret : %d \r\n", __key, len);

    return true;
}

static bool test_lindex(acl::redis_list &redis, int n)
{
    acl::string buf;

    redis.clear();
    bool ret = redis.lindex(__key, n, buf);
    if (ret == false) {
        printf("lindex key:%s error: %s\r\n", __key, redis.result_error());
        return false;
    } else
        printf("lindex key:%s, value:%s, ok\r\n", __key, buf.c_str());

    return true;
}

static bool test_lset(acl::redis_list &redis, int n)
{
    redis.clear();
    bool ret = redis.lset(__key, n, "new_value");
    if (ret == false) {
        printf("lset key:%s ,index:%d error: %s\r\n",
               __key, n, redis.result_error());
        return false;
    } else
        printf("lset key:%s ,index:%d ok \r\n", __key, n);

    return true;
}

static bool test_linsert_before(acl::redis_list &redis, int)
{
    redis.clear();
    int ret = redis.linsert_before(__key, "list1_1", "list1_new");
    if (ret < 0) {
        printf("linsert_before key:%s, error: %s\r\n",
               __key, redis.result_error());
        return false;
    } else
        printf("linsert_before key:%s,ok\r\n", __key);

    return true;
}

static bool test_linsert_after(acl::redis_list &redis, int)
{
    redis.clear();
    int ret = redis.linsert_after(__key, "list1_1", "list1_new");
    if (ret < 0) {
        printf("linsert_before key:%s, error: %s\r\n",
               __key, redis.result_error());
        return false;
    } else
        printf("linsert_before key:%s,ok\r\n", __key);

    return true;
}

static void usage(const char *procname)
{
    printf("usage: %s -h[help]\r\n"
           "-s redis_addr[127.0.0.1:6379]\r\n"
           "-n count\r\n"
           "-C connect_timeout[default: 10]\r\n"
           "-I rw_timeout[default: 10]\r\n"
           "-S [if slice request, default: no]\r\n"
           "-c [use cluster mode]\r\n"
           "-a cmd[lpush|rpush|lpushx|rpushx|lrange|rpop|lpop|blpop|brpop|rpoplpush|brpoplpush|lrem|ltrim|llen|lindex|lset|linsert_before|linsert_after]\r\n",
           procname);
}

int main(int argc, char *argv[])
{
    int ch, n = 50, conn_timeout = 10, rw_timeout = 10;
    acl::string addr("127.0.0.1:6379"), cmd("all");
    bool cluster_mode = false;

    acl::log::stdout_open(true);

    while ((ch = getopt(argc, argv, "hs:n:C:T:a:c")) > 0) {
        switch (ch) {
            case 'h':
                usage(argv[0]);
                return 0;
            case 's':
                addr = optarg;
                break;
            case 'n':
                n = atoi(optarg);
                break;
            case 'C':
                conn_timeout = atoi(optarg);
                break;
            case 'T':
                rw_timeout = atoi(optarg);
                break;
            case 'a':
                cmd = optarg;
                break;
            case 'c':
                cluster_mode = true;
                break;
            default:
                break;
        }
    }

    acl::acl_cpp_init();

    acl::redis_client_cluster cluster;
    cluster.set(addr.c_str(), 100, conn_timeout, rw_timeout);

    acl::redis_client client(addr.c_str(), conn_timeout, rw_timeout);

    acl::redis_list redis(&client);

    if (cluster_mode)
        redis.set_cluster(&cluster, 100);
    else
        redis.set_client(&client);

    bool ret;

    if (cmd == "lpush") {
        ret = test_lpush(redis, n)
              && test_lpush2(redis, n)
              && test_lpush3(redis, n)
              && test_lpush4(redis, n)
              && test_lpush5(redis, n)
              && test_lpush6(redis, n);
    } else if (cmd == "rpush")
        ret = test_rpush(redis, n)
              && test_rpush2(redis, n)
              && test_rpush3(redis, n)
              && test_rpush4(redis, n)
              && test_rpush5(redis, n)
              && test_rpush6(redis, n);
    else if (cmd == "lpushx")
        ret = test_lpushx(redis, n) && test_lpushx2(redis, n);
    else if (cmd == "rpushx")
        ret = test_rpushx(redis, n) && test_rpushx2(redis, n);
    else if (cmd == "lrange")
        ret = test_lrange(redis);
    else if (cmd == "rpop")
        ret = test_rpop(redis, n);
    else if (cmd == "lpop")
        ret = test_lpop(redis, n);
    else if (cmd == "blpop")
        ret = test_blpop(redis, n);
    else if (cmd == "brpop")
        ret = test_brpop(redis, n);
    else if (cmd == "rpoplpush")
        ret = test_rpoplpush(redis, n);
    else if (cmd == "brpoplpush")
        ret = test_brpoplpush(redis, n);
    else if (cmd == "lrem")
        ret = test_lrem(redis, n);
    else if (cmd == "ltrim")
        ret = test_ltrim(redis, n);
    else if (cmd == "llen")
        ret = test_llen(redis, n);
    else if (cmd == "lindex")
        ret = test_lindex(redis, n);
    else if (cmd == "lset")
        ret = test_lset(redis, n);
    else if (cmd == "linsert_before")
        ret = test_linsert_before(redis, 1);
    else if (cmd == "linsert_after")
        ret = test_linsert_after(redis, 1);
    else {
        ret = false;
        printf("unknown cmd: %s\r\n", cmd.c_str());
    }

    if (ret)
        printf("test OK!\r\n");
    else
        printf("test failed!\r\n");

#ifdef WIN32
    printf("enter any key to exit\r\n");
    getchar();
#endif

    return 0;
}
